package com.hongji.lms5xxcommunicator.realtime.sync;

/**
 * 设备相对时间戳(µs, 可能32-bit回绕) → 主机单调时钟(ns) 的在线映射。
 * 模型： hostNs ≈ a + b * devUsExt
 * - devUsExt：对设备µs做回绕展开后的单调计数
 * - a,b：用EMA平滑，自动跟踪慢漂移
 */
public final class TimebaseAligner {
    // 若文档说明是64-bit或单位不同，改这里即可
    private static final long MOD_US = 0x1_0000_0000L; // 假设32-bit µs
    private static final double ALPHA = 0.02;          // EMA平滑系数(0..1]
    private static final long MIN_DEV_SPAN_US  = 5_000;      // 5ms
    private static final long MIN_HOST_SPAN_NS = 1_000_000;  // 1ms

    private long lastDevUsRaw = Long.MIN_VALUE;
    private long wrapCount = 0;
    private long lastDevUsExt = Long.MIN_VALUE;
    private long lastHostNs   = Long.MIN_VALUE;

    private volatile double aNs = 0.0;
    private volatile double bNsPerUs = 1000.0; // 初值≈1e3 ns/µs
    private volatile boolean ready = false;

    public synchronized void observe(long devUsRaw, long hostNsNow) {
        if (lastDevUsRaw == Long.MIN_VALUE) {
            lastDevUsRaw = devUsRaw;
            lastDevUsExt = devUsRaw;
            lastHostNs    = hostNsNow;
            aNs = hostNsNow - bNsPerUs * devUsRaw;
            ready = true;
            return;
        }
        // 回绕展开
        if (devUsRaw < lastDevUsRaw && (lastDevUsRaw - devUsRaw) > (MOD_US >>> 1)) {
            wrapCount++;
        }
        long devUsExt = devUsRaw + wrapCount * MOD_US;

        long dUs = devUsExt - lastDevUsExt;
        long dNs = hostNsNow - lastHostNs;
        if (dUs >= MIN_DEV_SPAN_US && Math.abs(dNs) >= MIN_HOST_SPAN_NS) {
            double bInst = (double) dNs / (double) dUs;
            double aInst = hostNsNow - bInst * (double) devUsExt;
            bNsPerUs = (1 - ALPHA) * bNsPerUs + ALPHA * bInst;
            aNs      = (1 - ALPHA) * aNs      + ALPHA * aInst;
            ready = true;
        }

        lastDevUsRaw = devUsRaw;
        lastDevUsExt = devUsExt;
        lastHostNs   = hostNsNow;
    }

    /** 将设备µs时间(可能回绕)映射为主机ns；未就绪返回 Long.MIN_VALUE。 */
    public synchronized long toHostNs(long devUsRaw) {
        if (!ready) return Long.MIN_VALUE;
        long wraps = wrapCount;
        if (lastDevUsRaw != Long.MIN_VALUE &&
                devUsRaw < lastDevUsRaw &&
                (lastDevUsRaw - devUsRaw) > (MOD_US >>> 1)) {
            wraps++;
        }
        long devUsExt = devUsRaw + wraps * MOD_US;
        double hostNs = aNs + bNsPerUs * (double) devUsExt;
        return (long) Math.round(hostNs);
    }

    public boolean isReady() { return ready; }
}
